<script lang="ts" setup>
import { computed, nextTick, onMounted, ref, watch } from 'vue';

import loader from '@monaco-editor/loader';
import * as monaco from 'monaco-editor';
import prettier from 'prettier/standalone';

import { useEditorStore } from '@/store';

import * as prettierNginx from '../plugins/prettier-plugin-nginx';
import * as prettierToml from '../plugins/prettier-plugin-toml';
import { configMonocaEditor, registerLanguage } from './init';

defineOptions({
  name: 'CodeEditor',
});
const {
  ext = '.txt',
  height,
  name,
  toolbarHeight = 36,
  value,
} = defineProps<{
  ext: string;
  height: number;
  name: string;
  toolbarHeight: number;
  value: string;
}>();
const emits = defineEmits(['update:value', 'changed']);
loader.config({ monaco });
loader.config({ paths: { vs: 'monaco-editor/min/vs' } });
loader.config({
  'vs/nls': {
    availableLanguages: { '*': 'zh-cn' },
  },
});

registerLanguage();

const editorInstances = ref<Record<string, any>>({});
const editorModels = ref<Record<string, monaco.editor.ITextModel | null>>({});
let editorInstance: monaco.editor.IStandaloneCodeEditor | undefined;
const editorStyle = computed(() => {
  return {
    height: `${height - toolbarHeight}px`,
  };
});
const editorStore = useEditorStore();
const config = computed<monaco.editor.IStandaloneEditorConstructionOptions>(
  () => {
    return editorStore.options;
  },
);
let $monaco: typeof monaco;

const languages = monaco.languages.getLanguages();
const language = computed(() => {
  return (
    languages.find((item) => item.extensions?.includes(ext))?.id || 'plaintext'
  );
});

// 右键菜单选项
async function addAction() {
  const lang = language.value;
  configMonocaEditor($monaco);
  switch (lang) {
    case 'toml': {
      editorInstance?.addAction({
        contextMenuGroupId: 'navigation',
        contextMenuOrder: 1.5,
        id: 'editor.action.formatTomlDocument',
        keybindings: [
          monaco.KeyMod.Shift | monaco.KeyMod.Alt | monaco.KeyCode.KeyF,
        ],
        label: '格式化文档',
        run: (editor) => {
          const code = editor.getValue();
          prettier
            .format(code, {
              parser: 'toml',
              plugins: [prettierToml], // 根据需要添加
              printWidth: 80,
              semi: false,
              singleQuote: true,
              tabWidth: 4,
              trailingComma: 'all',
            })
            .then((formattedCode) => {
              editor.setValue(formattedCode);
            });
        },
      });

      break;
    }
    case 'nginx': {
      editorInstance?.addAction({
        contextMenuGroupId: 'navigation',
        contextMenuOrder: 1.5,
        id: 'editor.action.formatNginxDocument',
        keybindings: [
          monaco.KeyMod.Shift | monaco.KeyMod.Alt | monaco.KeyCode.KeyF,
        ],
        label: '格式化文档',
        run: (editor) => {
          const code = editor.getValue();
          prettier
            .format(code, {
              parser: 'nginx',
              plugins: [prettierNginx], // 根据需要添加
              printWidth: 80,
              semi: false,
              singleQuote: true,
              tabWidth: 4,
              trailingComma: 'all',
            })
            .then((formattedCode) => {
              editor.setValue(formattedCode);
            });
        },
      });

      break;
    }
    default: {
      // editorInstance?.addAction({
      //   contextMenuGroupId: 'navigation',
      //   contextMenuOrder: 1.5,
      //   id: 'editor.action.formatDocument',
      //   keybindings: [
      //     monaco.KeyMod.Shift | monaco.KeyMod.Alt | monaco.KeyCode.KeyF,
      //   ],
      //   label: '格式化文档',
      //   run: (editor) => {
      //     const code = editor.getValue();
      //     prettier
      //       .format
      //   }
      // })
      break;
    }
  }
}

onMounted(() => {
  nextTick(() => {
    // 初始化编辑器实例
    loader.init().then(async (_monacoEditor) => {
      $monaco = _monacoEditor;
      // 获取容器实例
      const editorContainer = document.querySelector(
        `.editor-${name}`,
      ) as HTMLElement;

      const options = {
        language: language.value,
        readOnly: false,
        value,
        ...config.value,
      } as monaco.editor.IStandaloneEditorConstructionOptions;

      setTimeout(async () => {
        if (editorContainer) {
          editorInstance = _monacoEditor.editor.create(
            editorContainer,
            options,
          );
          editorInstance?.onDidChangeModelContent(() => {
            const data = editorInstance?.getValue() as string;
            emits('update:value', data);
            // emits('changed', true, data);
          });

          addAction();

          editorInstances.value[name] = editorInstance;
          editorModels.value[name] = editorInstance.getModel();
        }
      }, 300);
    });
  });
});

const dispose = (name: string) => {
  if (editorInstances.value[name]) {
    editorModels.value[name]?.dispose();
    delete editorModels.value[name];
    delete editorInstances.value[name];
  }
};

watch(
  () => config.value,
  (newValue) => {
    editorInstance?.updateOptions(newValue);
  },
  { deep: true, immediate: true },
);

watch(
  () => value,
  (newValue) => {
    if (newValue !== editorInstance?.getValue()) {
      editorInstance?.setValue(newValue);
    }
  },
  { deep: true, immediate: true },
);

watch(
  () => language.value,
  (lang) => {
    if (editorInstance && $monaco) {
      $monaco?.editor.removeAllMarkers(lang);
      $monaco.editor.setModelLanguage(
        editorInstance.getModel() as monaco.editor.ITextModel,
        lang,
      );

      addAction();
    }
  },
  { immediate: true },
);

defineExpose({
  dispose,
});
</script>
<template>
  <div class="wos-editor__content">
    <div :style="editorStyle" class="editor-container">
      <div :class="`editor-${name}`" class="editor-container-box"></div>
    </div>
  </div>
</template>
<style lang="scss">
.editor-container-box {
  height: 100%;
}
</style>
